﻿#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>

/*int main()
{
  int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
  //打印数组内容
  //int＊ p = arr;//int* p = &arr[0];
  int sz = sizeof(arr) / sizeof(arr[0]);

  for(int i = 0;i < sz;i++)
  {
    printf("%d ",arr[i]);
  }
  return 0;
}*/
/*#include<stdio.h>

int main()
{
  int a = 20;

  //printf("%p\n",&a);
  int*p = &a;//地址 -- 指针变量
  //p 是指针变量 - 本身也是有地址的，作用是：存放指针（地址）的变量
  //指针变量是用来存放地址的
  //

  return 0;
}*/

/*#include<stdio.h>

int main()
{
  char ch = 'w';
  char*pc = &ch;

  printf("%d\n",*pc);

  return 0;
}*/
/*#include<stdio.h>
int main()
{
  int a =20;
  int*pa = &a;
  //* 是 解引用操作符（间接访问操作）
  //＊pa = 20
  *pa = 100;//替换
  printf("%d\n",a);//a = 100

  return 0;
}*/


/*#include<stdio.h>

int main()
{
  int a= 10;
  int*pa = &a;
  //%zd 用于打印 sizeof_t类型变量的正确占位符
  printf("%zd\n",sizeof(pa));//8
  printf("%zd\n",sizeof(int*));//8
  printf("%p\n",pa);//0x7fefb21f78

  return 0;
}*/

/*#include<stdio.h>

int main()
{
  char ch = 'w';
  char* pc = &ch;

  printf("%zd\n",sizeof(pc));//8
  printf("%zd\n",sizeof(char*));//8
  printf("%p\n",pc);//0x7ffa32f15b

  return 0;
}*/

//结论
/*1.指针 其实就是 地址
2.指针变量 是存放指针（地址）
口头语说的指针一般都是：指针变量 写作：int* p
pa 也是变量，是需要向内存申请一块空间，这样才有能力存放地址
//
pa 空间的大小是多少？指针变量的大小是多少？
//
指针变量需要要多大空间，是取决存放的是什么？（很明显存放的是地址）
地址存放需要多大的空间，指针变量的大小就是多大
//
在32位机器上：地址是32个 0/1的二进制序列，储存起来需要32个bit位，也就是4个字节，指针变量的大小就是4个字节
在64位机器上：地址是64个 0/1的二进制序列，储存起来需要64个bit位，也就是8个字节，指针变量的大小就是8个字节
//
也就是说：无论int 还是 char类型的数据，在x64的机器上，指针变量就是8个字节*/

/*#include<stdio.h>

int main()
{
  int a = 0x11223344;//
  char* pa = &a;
  printf("%p\n",pa);//0x7fd2094c28
  printf("%p\n",&a);//0x7fd2094c28
  printf("%d\n",*pa);//68


  return 0;
}*/
/*#include<stdio.h>

int main()
{
  int a = 10;
  int* pa = &a;
  char* pc = (char*)&a;//(char)是强制类型转换，因为a为整型（int），转换为char类型
  //
  printf("pa     = %p\n",pa);//0x7fe34a16c8
  printf("pa + 1 = %p\n",pa + 1);//0x7fe34a16cc
  //
  printf("pc     = %p\n",pc);//0x7fe34a16c8
  printf("pc + 1 = %p\n",pc + 1);//0x7fe34a16c9

  return 0;
}
结论：
指针类型决定了指针的步长，就是向前/向后走一步有多大的距离
int 类型的走一步就是 4个字节；而char 类型的走一步就是看一步个字节
type* p
p + i 是跳过type类型的数据，相当于跳过了i＊sizeof(type)个字节
所以：
int* p;
p + 2 相当于跳过了2个int类型的数据，相当于跳过了2＊sizeof(int) = 8个字节*/

/*#include<stdio.h>

int main()
{
  int a = 10;
  int* pa = &a;//int*
  char* pc = &a;//int *
  //printf("*pa = %d\n",*pa);
  //printf("*pc = %d\n",*pc);
  //void* pv = &a;//err
  //printf("pv = %d\n",*pv);
   double d = 3.14;
   double* pd = &d;
   printf("*pd = %lf\n",*pd);
  // void* pd = &d;//err
   //printf("*pd = %lf\n",*pd);

  return 0;
}*/
//用const修饰局部变量
/*#include<stdio.h>

int main()
{
  //int n = 10;//n是变量
  //n = 0;
  //printf("%d\n",n);
  const int n = 10;
  //const 修饰了 n 之后，n 就不能被修改了，但是n还是变量，被称为常变量
  //n = 0;编译错误

  printf("%d\n",n);

  return 0;
}*/

/*#include<stdio.h>

int main()
{
  const int n = 10;
  //n = 0;//err
  int* p = &n;
  *p = 20;//转换
  printf("n = %d\n",n);
  return 0;
}*/
/*#include<stdio.h>

int main()
{
    const int n = 10;
    //n = 0;//err

    int* p = &n;
    *p = 20;//还是改变不了n的值

    printf("%d\n", n);
    return 0;
}*/
//const 修饰指针变量有2种情况
/*1.const 放在＊的左边：限制的是＊p ，意思是不能通过p改变p所指向的对象的内容，但是p本身是可以改变的，p可以指向其他对象
const int* p = &n;
2.const 放在＊的右边：限制的是 P，意思是不能改变p本身的值，但是p指向的内容哦你是可以通过p来改变的
int* const p = &n;*/

/*#include<stdio.h>

int main()
{
    int n = 10;
    int m = 100;
    const int* p = &n;
    *p = 20;//err
    p = &m;//ok
    printf("n = %d\n",n);
    return 0;
}*/
/*#include<stdio.h>

int main()
{
   int n = 10;
   int m = 20;
   const int* p = &n;
   *p = 20;//ok?
   p = &m; //ok?
   return 0;
}*/
//指针运算
//指针的基本运算有三种，分别是：
//指针+-整数
//指针 - 指针
//指针的关系运算
#include<stdio.h>

//int main()
//{
//    int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
//    //打印数组的内容
//    //下标：0 1 2 3 4 5 6 7 8 9
//    int i = 0;
//    int sz = sizeof(arr) / sizeof(arr[0]);
//    //
//    for (i = 0; i < sz; i++);
//    {
//        printf("%d ", arr[i]);
//    }
//
//    return 0;
//}

/*#include<stdio.h>

int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    //打印数组的内容
    //下标：0 1 2 3 4 5 6 7 8 9
    int i = 0;
    int sz = sizeof(arr) / sizeof(arr[0]);

    for (i = 0; i < sz; i++)
    {
        printf("%d ", arr[i]);
    }

    return 0;
}*/


//打印数组的内容
/*#include<stdio.h>

int main()
{
    int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    //打印数组的内容
    //下标：1 2 3 4 5 6 7 8 9
    int i = 0;
    int sz = sizeof(arr) / sizeof(arr[0]);

    int* p = &arr[0];

    for(i = 0; i < sz; i++)
    {
        printf("%d ",*p);
        p++;
    }

    return 0;
}*/
/*#include<stdio.h>

int main()
{
    int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    //打印数组的内容
    //下标：0 1 2 3 4 5 6 7 8 9
    int i = 0;
    int sz = sizeof(arr)/sizeof(arr[0]);

    for(i = 0; i < sz; i++)
    {
        printf("%d ",arr[i]);
    }

    return 0;
}*/
/*#include<stdio.h>
int main()
{
    int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
    int i = 0;
    int sz = sizeof(arr)/sizeof(arr[0]);
    //把数组的首元素的地址存放在p中
    int* p = &arr[0];

    for(i = 0; i < sz; i++)
    {
        printf("%d ",*(p + i));
    }

    return 0;
}*/

//指针 - 指针的运算前提是：两个指针指向了同一块空间
/*#include<stdio.h>

int main()
{
    int arr[10] = {0};
    printf("%d\n",&arr[9] - &arr[0]);//9 指针 - 指针
    printf("%d\n",&arr[0] - &arr[9]);//-9
    //指针 - 指针的绝对值是指针和指针之间的元素个数
    char ch = {0};
    //printf("%d\n",&arr[9] - &char[4]);//err
     //指针一定要指向同一空间，int 与 char不是同一个类型
    return 0;
}*/

#include<stdio.h>
//模拟 strlen()函数的作用
/*int my_strlen(char* p)
{
    int count = 0;//计数

    while (*p != '\0')
    {
        count++;
        p++;//p + 整数
    }
    return count;
}*/

/*int my_strlen(char* p)
{
    char* p1 = p;

    while (*p != '\0')
    {
        p++;//p + 整数
    }

    return p - p1;
}

int main()
{
    char arr[] = "abcdef";
    //数组名其实是数组的首元素的地址
    //arr = &arr[0];
    int len = my_strlen(arr);
    printf("%d\n",len);

    return 0;
}*/
//指针大小比较
/*#include<stdio.h>

int main()
{
    int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    //在 sizeof()里面的arr表示的是整个数组
    int sz = sizeof(arr) / sizeof(arr[0]);
    //赋值
    int* p = &arr[0];

    while(p < arr + sz)//指针的关系元素
    {
        printf("%d ", *p);
        p++;
    }

    return 0;
}*/
//野指针
//概念：野指针就是指针指向的位置是不可知的（随机的、不正确的、没有明确限制的）
//很危险
//1.局部变量不初始化时，它的值就是随机值
//2.当指针指向的范围超出数组arr的范围时，这个指针就变成了野指针（越界访问）
//3.局部变量的访问 -- 指针指向的空间释放
/*#include<stdio.h>
int main()
{
    int* p;//未初始化
    *p = 20;

    printf("%d\n",*p);
    return 0;
}*/
/*#include<stdio.h>

int main()
{
    int arr[10] = { 0 };
    int* p = &arr[0];
    int i = 0;
    //
    for(i = 0; i <= 11; i++)
    {
        //当函数指向的范围超过数组arr的范围时，p就是野指针
        *(p++) = i;
        printf("%d ",*p);
        //0 0 0 0 0 0 0 0 0 0 -528973776 127
    }
    return 0;
}*/
//指针指向的空间释放

/*#include<stdio.h>

int test()
{
    int n = 100;
    return &n;
}

int main()
{
    int* p = test();
    printf("%d\n",*p);//jiagu info: vpid 1: terminated with signal 11
    return 0;         //段错误
}*/
//如何规避野指针
//1.指针的初始化
//2.小心指针越界
//3.指针不再使用时，及时置NULL，指针使用之前检查有效性
//4.避免返回局部变量的地址
// 
//#include<stdio.h>

/*int main()
{
    int a = 10;
    int* pa = &a;
    *pa = 20;
    //打印
    printf("a = %d\n",a);

    return 0;
}*/
//如果明确知道指针指向哪⾥就直接赋值地址，如果不知道指针应该指向哪⾥，可以给指针赋值NULL.
//NULL 是C语⾔中定义的⼀个标识符常量，值是0，0也是地址，这个地址是⽆法使⽤的，读写该地址
//会报错
/*int main()
{
    int a ;
    int* p = NULL;
    *p = 20;
    printf("%\n",a);
    return 0;
}*/
/*#include<stdio.h>

int main()
{
    int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    int* p = &arr[0];
    int sz = sizeof(arr) / sizeof(arr[0]);
    int i = 0;
    //
    for(i = 0; i < sz; i++)
    {
        *(p++) = i;
    }
    //此时p已经越界了，可以把p置为NULL
    //P = NULL;
    //先判断，后使用
    p = &arr[0];
    if(p != NULL)
    {
        //……
    }
    return 0;
}*/
//assert断言
//assert()的头文件是assert.h ,⽤于在运⾏时确保程序符合指定条件，如果不符合，就报
//错终⽌运⾏。这个宏常常被称为“断⾔”。
//形式：assert(p != NULL);
/*#include<stdio.h>

int main()
{
    int a = 10;
    int* p = &a;
    assert(p != NULL);
    *p = 20;

    printf("%d\n",a);
    return 0;
}*/
/*#include<stdio.h>
#include<assert.h>
//asert()的头文件的是 assert.h
int main()
{
    int a = 10;
    int* p = &a;
    assert(p != NULL);//这个是可以
    *p = 20;
    printf("%d\n",a);
    return 0;
}*/
/*#include<stdio.h>

int main()
{
    int a = 10;
    int* p = NULL;
    assert(p != NULL);
    *p = 20;
    printf("%d\n",a);


    return 0;
}*/

/*#include<stdio.h>
#include<assert.h>

size_t my_strlen(const char*s)
{
    size_t count = 0;//计数
    assert(s != NULL);//判断一下

    while(*s != '\0');
    {
        count++;
        s++;
    }
    return count;
}
int main()
{
    //strlen() - 求字符串长度的（求字符串中/0之前的字符的个数）
    char arr[] = "abcdef";
    //size_t() 求的是无符号的数据
    size_t len = my_strlen(arr);

    return 0;
}*/
/*#include<stdio.h>
#include<assert.h>

size_t my_strlen(char* s)
{
    ssize_t count = 0;
    assert(s != NULL);
    //
    while(*s != '\0')
    {
        count++;
        s++;
    }

    return count;
}

int main()
{
    char arr[] = "abcdef";
    size_t len = my_strlen(arr);

    printf("%zd\n",len);

    return 0;
}*/
/*#include<stdio.h>

void Swap(int a,int b)
{
    int z = 0;
    z = a;
    a = b;
    b = z;
}
int main()
{
    int a = 10;
    int b = 20;
    //
    printf("交换前： a = %d b = %d\n",a,b);
    //交换
    Swap(a,b);
    printf("交换后： a = %d b = %d\n",a,b);
    return 0;
}*/
//#include<stdio.h>
////改变地址里面的值，不是交换地址
//void Swap(int* a,int* b)
//{
//    int z = 0;
//    z = *a;
//    *a = *b;
//    *b = z;
//}
//
//int main()
//{
//    int a = 10;
//    int b = 20;
//    //
//    printf("交换前： a = %d b = %d\n",a,b);
//      //交换
//    Swap(&a,&b);
//    printf("交换后： a = %d b = %d\n",a,b);
//    return 0;
//}

//知识点：
//数组名是数组首元素的地址 ，但是有两个例外：
//1.sizeof(数组名)这里的地址表示的是整个数组，计算的是整个数组的大小
//2.＆数组名也表示的是整个数组，取出的是整个数组的地址
//除此之外：所有的数组名都是数组的首元素的地址

/*#include<stdio.h>

int main()
{
    int arr[] = {1,2,3,4,5,6,7,8,9,10};
    //arr = &arr[0]
    printf("arr     = %p\n",arr);//0x7fe476b114
    printf("arr + 1 = %p\n",arr + 1);//0x7fe476b118
    //arr + 1 和 ＆arr[0] +1 都是跳过一个整型（int 4个字节）
    printf("&arr[0]     = %p\n",&arr[0]);//0x7fe476b114
    printf("&arr[0] + 1 = %p\n",&arr[0] + 1);//0x7fe476b118
    //&arr 取的是整个数组的地址  ＆arr + 1 就是跳过了一个整型的地址大小（）
    printf("&arr     = %p\n",&arr);//0x7fe476b114//16进制
    printf("&arr + 1 = %p\n",&arr + 1);//0x7fe476b13c//两个相差了40个字节 就是10个数据

    return 0;
}*/
/*#include<stdio.h>
int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    //打印数组的内容
    int* p = arr;//int*p = &arr[0];
    int sz = sizeof(arr) / sizeof(arr[0]);
    int i = 0;
    //给数组输入10个值
    for(i = 0; i < sz; i++)
    {
        scanf("%d",&arr[i]);
        //scanf("%d",p + i);
    }
    //输出
    for(i = 0; i < sz; i++)
    {
        printf("%d ",*(p + i));
    }

    return 0;
}*/
// 一维数组传参的时候，传过去的是数组的首元素的地址
//形参的部分可以写成指针的形式，也可以写成数组的形式，写成数组的形式是为了方便理解
/*#include<stdio.h>
//

void test(int arr[])//err
{         //int arr[] = int* arr//数组传参本质上是指针
    int sz2 = sizeof(arr)/sizeof(arr[0]);
    //
    printf("sz2 = %d\n",sz2);//sz2 = 2
}

int main()
{
    int arr[10] = {0};
    int sz1 = sizeof(arr)/sizeof(arr[0]);
    //
    printf("sz1 = %d\n",sz1);//sz1 = 10
    //                       //sz2 = 2 ??
    test(arr);

    return 0;
}*/
//排列算法：

//冒泡排序：
//思想：
//1.两两相邻的元素进行比较
//2.如果不满足顺序就交换，满足顺序就找下一对
//选择排序
//插入排序
//快速排序
//堆排序
//希尔排序

/*#include<stdio.h>

void bubble_sort(int arr[],int sz)
{
    int i = 0;
    //i 表示多少趟数
    for(i = 0; i < sz; i++)
    {
        //一趟冒泡排序
        int j = 0;

        for(j = 0; j < sz - 1 - i; j++)
        {
            if(arr[j] > arr[j + 1])
            {
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;

            }
        }
    }
}

void print_arr(int arr[],int sz)
{
    int i = 0;

    for(i = 0; i < sz; i++)
    {
        printf("%d ",arr[i]);
    }
    printf("\n");
}

int main()
{
    int arr[10] = {10,9,8,7,6,5,4,3,2,1};
    //实现排序，拍成升序
    int sz = sizeof(arr)/sizeof(arr[0]);
    //打印函数
    print_arr(arr,sz);
    //排序函数
    bubble_sort(arr,sz);
    //打印排序完的数组
    print_arr(arr,sz);

    return 0;
}*/

/*#include<stdio.h>

void print_arr(int arr[],int sz)
{
    int i = 0;

    for(i = 0;i < sz; i++)
    {
        printf("%d ",arr[i]);
    }
    printf("\n");
}

void bubble(int* arr,int sz)
{
    int i = 0;
    //i 就表示多少趟排序
    for(i = 0; i < sz; i++)
    {
        int j = 0;//一趟冒泡排序，就得出一个最大值
        for(j = 0; j < sz - 1 - i; j++)
        {
            if(*(arr + j) > *(arr + j + 1))
            {
                int tmp = *(arr + j);
                *(arr + j) = *(arr + j +1);
                *(arr + j + 1) = tmp;

            }
        }

    }
}

int main()
{
    int arr[] = {10,9,8,7,6,5,4,3,2,1};//
    int sz = sizeof(arr)/sizeof(arr[0]);
    //打印函数
    print_arr(arr,sz);
    //升序函数
    bubble_sort(arr,sz);
    //打印升序之后的
    print_arr(arr,sz);

    return 0;
}*/